package com.dfe.backend.common.websocket.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dfe.backend.common.bean.virtual.VirtualReference;
import com.dfe.backend.common.cache.CaffeineCache;
import com.dfe.backend.common.util.JsonValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import java.net.URI;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 【描述】：WebSocket客户端
 *
 * @author LiChang
 * @since 2025-8-14 14:32
 */
@Slf4j
@Component
@ClientEndpoint
public class WebSocketClient {

    private Session session;

    @Value("${config.websocket.serverHost}")
    private String serverHost;
    private final WebSocketContainer container;
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    private static final int MAX_RETRIES = 10;
    private static final long INITIAL_DELAY_MS = 1000;
    private static final long MAX_DELAY_MS = 60000;
    private int retryCount = 0;
    private volatile boolean manualClose = false;
    private final CaffeineCache caffeineCache;

    public WebSocketClient(CaffeineCache caffeineCache) {
        this.caffeineCache = caffeineCache;
        this.container = ContainerProvider.getWebSocketContainer();
    }

    @PostConstruct
    public void connect() {
        if (!manualClose) {
            try {
                URI serverUri = new URI("ws://" + serverHost + "/ws");
                log.info("Connecting to: " + serverUri);
                session = container.connectToServer(this, serverUri);
                retryCount = 0; // 连接成功后重置重试计数
            } catch (Exception e) {
                handleConnectionError(e);
            }
        }
    }


    /**
     * 【描述】: 连接打开时调用
     *
     * @param session session
     * @author LiChang
     * @since 2025-8-22 11:45
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        log.info("Connected to server");
        try {
            // 发送消息到服务器
            session.getBasicRemote().sendText("Hello from Java Client!");
        } catch (Exception e) {
            log.error("连接开启，发送消息失败: " + e.getMessage());
        }
    }

    /**
     * 【描述】: 接收消息时调用
     *
     * @param message message
     * @param session session
     * @author LiChang
     * @since 2025-8-22 11:46
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        // 处理服务器消息
        log.info("Received from server: " + message);
        if (JsonValidator.isJsonValid(message) && message.contains("ref")) {
            // String转json
            JSONObject json = JSON.parseObject(message);
            if (json.containsKey("ref")) {
                caffeineCache.virtualReferenceCache().put(json.getString("ref"), VirtualReference.builder().value(json.getString("value")).status(1).build());
            }
        }
    }

    /**
     * 【描述】: 接收消息时调用
     *
     * @param session     session
     * @param closeReason closeReason
     * @author LiChang
     * @since 2025-8-22 11:46
     */
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("Connection closed: " + closeReason);
        this.session = null;
        if (!manualClose) {
            scheduleReconnect();
        }
    }

    /**
     * 【描述】: 连接错误时调用
     *
     * @param session   session
     * @param throwable throwable
     * @author LiChang
     * @since 2025-8-22 11:46
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("WebSocket error: " + throwable.getMessage());
        if (session != null && session.isOpen()) {
            try {
                session.close();
            } catch (Exception e) {
                // 忽略关闭异常
            }
        }
        if (!manualClose) {
            scheduleReconnect();
        }
    }

    /**
     * 【描述】: 给服务端发送信息
     *
     * @param message message
     * @author LiChang
     * @since 2025-8-15 11:33
     */
    public void sendMessage(String message) {
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                log.error("Failed to send message: " + e.getMessage());
            }
        } else {
            log.error("Cannot send message - connection not open");
        }
    }

    public void close() {
        manualClose = true;
        if (session != null && session.isOpen()) {
            try {
                session.close();
            } catch (Exception e) {
                // 忽略关闭异常
            }
        }
        executor.shutdown();
    }

    private void handleConnectionError(Exception e) {
        log.error("Connection failed: " + e.getMessage());
        if (!manualClose) {
            scheduleReconnect();
        }
    }

    private void scheduleReconnect() {
        if (retryCount < MAX_RETRIES) {
            // 指数退避算法计算延迟时间
            long delay = Math.min(INITIAL_DELAY_MS * (1L << retryCount), MAX_DELAY_MS);
            retryCount++;

            System.out.printf("Reconnecting attempt %d/%d in %dms...%n",
                    retryCount, MAX_RETRIES, delay);

            executor.schedule(this::connect, delay, TimeUnit.MILLISECONDS);
        } else {
            System.err.println("Max reconnect attempts reached. Giving up.");
        }
    }

    // 启动客户端
    public static void main(String[] args) {
        try {
//            URI uri = new URI("ws://127.0.0.1:8080/websocket/user"); // 测试服务器
//            WebSocketClient client = new WebSocketClient(caffeineCache);
//            client.connect();
//
//            // 保持主线程运行
//            Thread.sleep(Long.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
